package com.iwdnb.gkgz.application.utils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.iwdnb.bmnf.common.utils.BeanConvertUtils;
import com.iwdnb.bmnf.common.utils.Timer;
import com.iwdnb.gkgz.application.model.vo.StrategyTradeScore;
import com.iwdnb.gkgz.application.model.vo.StrategyTradeScore.TradeScoreItem;
import com.iwdnb.gkgz.application.model.vo.StrategyTradeScoreInfo;
import com.iwdnb.gkgz.common.model.dto.Stock;
import com.iwdnb.gkgz.common.model.dto.StrategyTradeDTO;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

@Slf4j
public class StockScoreUtils {

    public static final Map<String, Function<StrategyTradeDTO, String>> functionMap = new LinkedHashMap<>();

    public static Map<String, List<StrategyTradeDTO>> statisticTradeList(String group,
        String parentGroup, List<StrategyTradeDTO> strategyTradeList) {
        if (CollectionUtils.isEmpty(strategyTradeList)) {
            return null;
        }
        Function<StrategyTradeDTO, String> groupFunction = getFunction(group);
        Map<String, List<StrategyTradeDTO>> map = strategyTradeList.stream().collect(
            Collectors.groupingBy(groupFunction));
        return formatStatisticMap(group, parentGroup, map);
    }

    public static StrategyTradeScoreInfo calculateTradeScore(StrategyTradeDTO strategyTradeDTO,
        List<StrategyTradeScore> tradeScoreList) {
        List<String> groupList = tradeScoreList.stream().map(StrategyTradeScore::getGroup).collect(
            Collectors.toList());
        List<StrategyTradeScore> scores = new ArrayList<>();
        for (int i = 0; i < groupList.size(); i++) {
            String group = groupList.get(i);
            String label = StockScoreUtils.getGroupLabel(group, strategyTradeDTO);
            StrategyTradeScore strategyTradeScore = calculateTradeScore(group, label, tradeScoreList);
            scores.add(strategyTradeScore);

        }
        StrategyTradeScoreInfo scoreInfo = new StrategyTradeScoreInfo();
        scoreInfo.setScoreList(scores);
        BigDecimal sumScore = scores.stream().map(t -> t.getItems().get(0).getAvgRate()).reduce(BigDecimal.ZERO,
            BigDecimal::add);
        scoreInfo.setScore(BigDecimalUtils.divide(sumScore, BigDecimalUtils.of(scores.size())));
        return scoreInfo;
    }

    public static String getGroupLabel(String group, StrategyTradeDTO strategyTradeDTO) {
        if (Objects.isNull(strategyTradeDTO)) {
            return "-";
        }
        Function<StrategyTradeDTO, String> groupFunction = getFunction(group);
        return groupFunction.apply(strategyTradeDTO);
    }

    public static Map<String, List<StrategyTradeDTO>> formatStatisticMap(String group, String parentGroup,
        Map<String, List<StrategyTradeDTO>> statisticMap) {
        if (MapUtils.isEmpty(statisticMap)) {
            return statisticMap;
        }
        Map<String, List<StrategyTradeDTO>> map = new LinkedHashMap<>();
        for (Entry<String, List<StrategyTradeDTO>> entry : statisticMap.entrySet()) {
            String key = group + ":" + entry.getKey();
            if (StringUtils.isNotBlank(parentGroup)) {
                key = parentGroup + "," + key;
            }
            map.put(key, entry.getValue());
        }
        return map;
    }

    public static final Function<StrategyTradeDTO, String> getFunction(String group) {
        if (StringUtils.equals(group, "stockImageValue")) {
            return stockImageValueFunction;
        } else if (StringUtils.equals(group, "stockSecondIndustry")) {
            return stockSecondIndustryFunction;
        } else if (StringUtils.equals(group, "tradeNumRatio")) {
            return tradeNumRatioFunction;
        } else if (StringUtils.equals(group, "period")) {
            return periodFunction;
        } else if (StringUtils.equals(group, "afterOneRate")) {
            return afterOneRateFunction;
        } else if (StringUtils.equals(group, "afterOneUpLineRate")) {
            return afterOneUpLineRateFunction;
        } else if (StringUtils.equals(group, "avg10Rate")) {
            return avg10RateFunction;
        } else if (StringUtils.equals(group, "avg20Rate")) {
            return avg20RateFunction;
        } else if (StringUtils.equals(group, "avg30Rate")) {
            return avg30RateFunction;
        } else if (StringUtils.equals(group, "avg60Rate")) {
            return avg60RateFunction;
        } else if (StringUtils.equals(group, "buyAvg10Rate")) {
            return buyAvg10RateFunction;
        } else if (StringUtils.equals(group, "buyAvg20Rate")) {
            return buyAvg20RateFunction;
        } else if (StringUtils.equals(group, "buyAvg30Rate")) {
            return buyAvg30RateFunction;
        } else if (StringUtils.equals(group, "buyAvg60Rate")) {
            return buyAvg60RateFunction;
        } else if (StringUtils.equals(group, "buyRate")) {
            return buyRateFunction;
        } else if (StringUtils.equals(group, "buyMinRate")) {
            return buyMinRateFunction;
        } else if (StringUtils.equals(group, "beforeTwentyLimitUpCount")) {
            return beforeTwentyLimitUpCountFunction;
        } else if (StringUtils.equals(group, "score")) {
            return scoreFunction;
        }

        return null;
    }

    public static final Function<StrategyTradeDTO, String> stockImageValueFunction = tradeDTO -> {
        String code = tradeDTO.getStockImageCode();
        return StringUtils.isNotBlank(code) ? code : "0";
    };

    public static final Function<StrategyTradeDTO, String> stockSecondIndustryFunction = tradeDTO -> {
        Stock stock = StockUtils.getStock(tradeDTO.getCode());
        return stock.getSecondIndustry() == null ? "0" : stock.getSecondIndustry();
    };

    public static final Function<StrategyTradeDTO, String> tradeNumRatioFunction
        = tradeDTO -> tradeDTO.getTradeNumRatioCode();

    public static final Function<StrategyTradeDTO, String> afterOneRateFunction = tradeDTO -> {
        //涨停次日涨幅
        BigDecimal rate = tradeDTO.getAfterOneRate();
        if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuNine)) {
            return "-10~-9";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuEight)) {
            return "-9~-8";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuSeven)) {
            return "-8~-7";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuSix)) {
            return "-7~-6";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuFive)) {
            return "-6~-5";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuFour)) {
            return "-5~-4";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuThree)) {
            return "-4~-3";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuTwo)) {
            return "-3~-2";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuOne)) {
            return "-2~-1";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.zero)) {
            return "-1~0";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.one)) {
            return "0~1";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.two)) {
            return "1~2";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.three)) {
            return "2~3";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.four)) {
            return "3~4";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.five)) {
            return "4~5";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.six)) {
            return "5~6";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.seven)) {
            return "6~7";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.eight)) {
            return "7~8";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.nine)) {
            return "8~9";
        } else if (BigDecimalUtils.isGt(rate, BigDecimalUtils.nine)) {
            return "9~10";
        }
        return "error";
    };

    public static final Function<StrategyTradeDTO, String> afterOneUpLineRateFunction = tradeDTO -> {
        //涨停次日涨幅
        BigDecimal rate = tradeDTO.getAfterOneUpLineRate();
        //if (BigDecimalUtils.isLe(rate, BigDecimalUtils.zeroPointFive)) {
        //    return "0~0.5";
        //}else
        if (BigDecimalUtils.isLe(rate, BigDecimalUtils.one)) {
            return "0~1";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.two)) {
            return "1~2";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.three)) {
            return "2~3";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.four)) {
            return "3~4";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.five)) {
            return "4~5";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.six)) {
            return "5~6";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.seven)) {
            return "6~7";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.eight)) {
            return "7~8";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.nine)) {
            return "8~9";
        } else if (BigDecimalUtils.isGt(rate, BigDecimalUtils.nine)) {
            return "9~10";
        }
        return "error";
    };

    public static final Function<StrategyTradeDTO, String> avg10RateFunction = tradeDTO -> {
        //涨停次日与涨停前三日的10日均线差值
        BigDecimal rate = tradeDTO.getAvgRate10();
        return getBuyAvgRateDescription(rate);
    };

    public static final Function<StrategyTradeDTO, String> avg20RateFunction = tradeDTO -> {
        //涨停次日与涨停前三日的20日均线差值
        BigDecimal rate = tradeDTO.getAvgRate20();
        return getBuyAvgRateDescription(rate);
    };

    public static final Function<StrategyTradeDTO, String> avg30RateFunction = tradeDTO -> {
        //涨停次日与涨停前三日的30日均线差值
        BigDecimal rate = tradeDTO.getAvgRate30();
        return getBuyAvgRateDescription(rate);
    };

    public static final Function<StrategyTradeDTO, String> avg60RateFunction = tradeDTO -> {
        //涨停次日与涨停前三日的60日均线差值
        BigDecimal rate = tradeDTO.getAvgRate60();
        return getBuyAvgRateDescription(rate);
    };

    public static final Function<StrategyTradeDTO, String> buyAvg10RateFunction = tradeDTO -> {
        //买入价与买入时均线差值
        BigDecimal rate = tradeDTO.getBuyAvgRate10();
        return getBuyAvgRateDescription(rate);
    };

    public static final Function<StrategyTradeDTO, String> buyAvg20RateFunction = tradeDTO -> {
        //买入价与买入时均线差值
        BigDecimal rate = tradeDTO.getBuyAvgRate20();
        return getBuyAvgRateDescription(rate);
    };

    public static final Function<StrategyTradeDTO, String> buyAvg30RateFunction = tradeDTO -> {
        //买入价与买入时均线差值
        BigDecimal rate = tradeDTO.getBuyAvgRate30();
        return getBuyAvgRateDescription(rate);
    };

    public static final Function<StrategyTradeDTO, String> buyAvg60RateFunction = tradeDTO -> {
        //买入价与买入时均线差值
        BigDecimal rate = tradeDTO.getBuyAvgRate60();
        return getBuyAvgRateDescription(rate);
    };

    public static final Function<StrategyTradeDTO, String> beforeTwentyLimitUpCountFunction = tradeDTO ->
        tradeDTO.getBeforeTwentyLimitUpCount() + "";

    public static final Function<StrategyTradeDTO, String> buyMinRateFunction = tradeDTO -> {
        //买入日最低价与买入价差
        BigDecimal rate = tradeDTO.getBuySignalMinRate();
        if (BigDecimalUtils.isLe(rate, BigDecimalUtils.zero)) {
            return "0";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.zeroPointFive)) {
            return "0~0.5";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.one)) {
            return "0.5~1";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.onePointFive)) {
            return "1~1.5";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.two)) {
            return "1.5~2";
        } else if (BigDecimalUtils.isGt(rate, BigDecimalUtils.two)) {
            return "2~5";
        }
        return "error";
    };

    public static final Function<StrategyTradeDTO, String> buyRateFunction = tradeDTO -> {
        //买入价与信号价差
        BigDecimal rate = tradeDTO.getBuySignalRate();
        if (BigDecimalUtils.isLe(rate, BigDecimalUtils.four)) {
            return "3~4";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.five)) {
            return "4~5";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.six)) {
            return "5~6";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.seven)) {
            return "6~7";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.eight)) {
            return "7~8";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.nine)) {
            return "8~9";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.ten)) {
            return "9~10";
        } else if (BigDecimalUtils.isGt(rate, BigDecimalUtils.ten)) {
            return "10~20";
        }
        return "error";
    };

    public static final Function<StrategyTradeDTO, String> periodFunction = tradeDTO -> tradeDTO.getBuyPeriod();

    public static final String getBuyAvgRateDescription(BigDecimal rate) {
        if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuNine)) {
            return "-10~-9";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuEight)) {
            return "-9~-8";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuSeven)) {
            return "-8~-7";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuSix)) {
            return "-7~-6";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuFive)) {
            return "-6~-5";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuFour)) {
            return "-5~-4";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuThree)) {
            return "-4~-3";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuTwo)) {
            return "-3~-2";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.fuOne)) {
            return "-2~-1";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.zero)) {
            return "-1~0";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.one)) {
            return "0~1";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.two)) {
            return "1~2";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.three)) {
            return "2~3";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.four)) {
            return "3~4";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.five)) {
            return "4~5";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.six)) {
            return "5~6";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.seven)) {
            return "6~7";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.eight)) {
            return "7~8";
        } else if (BigDecimalUtils.isLe(rate, BigDecimalUtils.nine)) {
            return "8~9";
        } else if (BigDecimalUtils.isGt(rate, BigDecimalUtils.nine)) {
            return "9~10";
        }
        return "error";
    }

    public static final Function<StrategyTradeDTO, String> scoreFunction = tradeDTO -> {
        //回测评分
        BigDecimal score = tradeDTO.getScore();
        if (BigDecimalUtils.isLe(score, BigDecimalUtils.onePointFive)) {
            return "1~1.5";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.onePointSix)) {
            return "1.5~1.6";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.onePointSeven)) {
            return "1.6~1.7";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.onePointEight)) {
            return "1.7~1.8";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.onePointNine)) {
            return "1.8~1.9";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.two)) {
            return "1.9~2.0";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointOne)) {
            return "2.0~2.1";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointTwo)) {
            return "2.1~2.2";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointThree)) {
            return "2.2~2.3";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointFour)) {
            return "2.3~2.4";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointFive)) {
            return "2.4~2.5";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointSix)) {
            return "2.5~2.6";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointSeven)) {
            return "2.6~2.7";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointEight)) {
            return "2.7~2.8";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.twoPointNine)) {
            return "2.8~2.9";
        } else if (BigDecimalUtils.isLe(score, BigDecimalUtils.three)) {
            return "2.9~3.0";
        } else if (BigDecimalUtils.isGt(score, BigDecimalUtils.seven)) {
            return "3.0~10";
        }
        return "error";
    };


    /**
     * 计算单项得分
     *
     * @param group
     * @param tradeMap
     * @param tradeScoreList
     * @return
     */
    private static StrategyTradeScore calculateTradeScore(String group, String label,
        List<StrategyTradeScore> tradeScoreList) {
        StrategyTradeScore strategyTradeScore = new StrategyTradeScore();
        strategyTradeScore.setGroup(group);
        StrategyTradeScore compareScore = tradeScoreList.stream().filter(t -> t.getGroup().equals(group)).findFirst()
            .get();
        TradeScoreItem compareItem = compareScore.getItems().stream().filter(t -> t.getLabel().equals(label)).findAny()
            .orElse(null);
        TradeScoreItem item = null;
        if (compareItem == null) {
            item = new TradeScoreItem();
            item.setLabel(label);
            item.setCount(1);
            item.setSumRate(BigDecimal.ONE);
            item.setAvgRate(BigDecimal.ONE);
        } else {
            item = BeanConvertUtils.convert(compareItem, TradeScoreItem.class);
        }
        strategyTradeScore.setItems(Lists.newArrayList(item));
        return strategyTradeScore;
    }
}
